Cloud Run FunctionsでサーバレスVPCアクセスコネクタ経由でCloud DNSの限定公開のプライベートゾーンのレコードを解決できるか試してみた

Cloud Run FunctionsでサーバレスVPCアクセスコネクタ経由でCloud DNSの限定公開のプライベートゾーンのレコードを解決できるか試してみた

Clock Icon2025.01.10

概要

Cloud DNSではインターネット上から名前解決できる公開ゾーン以外に、Google Cloud内だけで名前解決できる限定公開のプライベートゾーンを作成することができます。
以前Cloud DNSの限定公開のプライベートゾーンを作成して、指定したVPC内だけで名前解決できるかどうかを検証したことがあるのですが、その時はVMインスタンスで検証をしていました。
https://dev.classmethod.jp/articles/20241030-clouddns-privatezone/

今回はCloud Run Functionsでも同様に限定公開のプライベートゾーンの名前解決をできるかどうかを実際に検証してみました。
やりたいことのイメージとしては以下になります。

cloud_dns_tet.jpg

Cloud DNSで限定公開プライベート(CNAMEで公開ゾーンのドメインを設定)します。
Cloud Run Functionsで限定公開のプライベートゾーンのCNAMEから公開ゾーンのAレコードが弾けるかどうかを試してみます。

Cloud Run Functionsはそのまま実行するとVPC外で実行されるのでサーバレスVPCアクセスコネクタまたはDirect VPC Egressを設定する必要があります。
今回は伝統のサーバレスVPCアクセスコネクタを設定して、VPC内にCloud Run Functionsのトラフィックが流れるようにして名前解決ができるかどうか確認してみました。

やってみる

準備するものとしては以下となります。

  • Cloud Run Functions
  • サーバレスVPCアクセスコネクタ

※Cloud DNSの限定公開のプライベートゾーン、公開ゾーンは作成済みの前提です。限定公開のプライベートゾーンはdefault VPCで名前解決できるように指定しています。

サーバレスVPCアクセスコネクタの準備

terraformで作成します。

resource "google_compute_subnetwork" "vpc_connector_subnet" {
  name          = "vpc-connector-subnetwork"
  ip_cidr_range = "192.0.0.0/28"
  region        = "asia-northeast1"
  network       = "default"
}
resource "google_vpc_access_connector" "connector" {
  name          = "vpc-connector"
  min_instances = 2
  max_instances = 3
  subnet {
    name = google_compute_subnetwork.vpc_connector_subnet.name
  }
}
provider "google" {
  project = "プロジェクト名"
  region  = "asia-northeast1"
}

terraform {
  required_version = "1.10.3"

  required_providers {
    google = {
      source = "hashicorp/google"
      # provider version
      version = "~> 5.0"
    }
  }
}

ポイントとしては、今回すでに設定済みのCloud DNSの限定公開のプライベートゾーンの名前解決ができるVPCはdefault VPCを設定しているのでサーバレスVPCアクセスコネクタが用いるサブネットはdefault VPCに作成しています。
またサーバレスVPCアクセスコネクタが使用できるサブネットのCIDR範囲は/28以上になるのでそれも踏まえて192.0.0.0/28を設定しています。
サーバレスVPCアクセスコネクタのリソース設定でmachime_type(マシンタイプ)を省略していますが、省略した場合は最も小さいタイプe2-microで作成されます。
他にも設定値があるのでリファレンスも合わせて確認ください。
https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/vpc_access_connector

Cloud Run Functionsの準備

実行するCloud Run FunctionsもTerraformで作成します。

resource "google_cloudfunctions2_function" "function" {
  name = "dns-query-test"
  location = "asia-northeast1"
  description = "dns query test"
  build_config {
    runtime = "python312"
    entry_point = "dns_test"
    source {
      storage_source {
        bucket = "バケット名"
        object = "zipファイル名"
      }
    }
  }

  service_config {
    max_instance_count  = 1
    available_memory    = "256M"
    timeout_seconds     = 60
    vpc_connector       = "サーバレスVPCアクセスコネクタ名"
  }
}

TerraformからCloud Run FunctionsをデプロイするにはCloud Storageにzipでソース(main.py、requirements.txt)を圧縮して保存します。
またvpc_connectorに先ほど作成したサーバレスVPCアクセスコネクタ名を指定することでCloud Run FunctionsのトラフィックがVPCに流れるようになります。

サーバレスVPCアクセスコネクタが作成したCloud Run Functionsに設定されていると詳細タブで以下の通り表示されます。
スクリーンショット 2025-01-10 0.31.30.png

デプロイする検証用のpythonスクリプトは以下です。

main.py
iimport functions_framework
import dns.resolver

@functions_framework.http
def dns_test(request):
    domain = "限定公開プライベートゾーンのDNS名を指定"
    try:
        # CNAMEレコードを取得
        cname_answers = dns.resolver.resolve(domain, 'CNAME')
        for cname_rdata in cname_answers:
            cname_target = cname_rdata.target.to_text()
            print(f"CNAME Record: {domain} -> {cname_target}")

            # CNAMEの先のAレコードを取得
            a_answers = dns.resolver.resolve(cname_target, 'A')

            # Aレコードの値をリストに収集
            a_records = [a_rdata.to_text() for a_rdata in a_answers]
            print(f"A Records for {cname_target}: {a_records}")

            # Aレコードをレスポンスとして返す
            return {
                "domain": domain,
                "cname_target": cname_target,
                "a_records": a_records
            }

    except dns.resolver.NXDOMAIN:
        # ドメインが存在しない場合
        return 'Domain does not exist.', 404

    except dns.exception.Timeout:
        # DNSクエリがタイムアウトした場合
        return 'DNS query timed out.', 504

    except Exception as e:
        # その他のエラー
        return f'An error occurred: {str(e)}', 500
functions-framework==3.*
dnspython

検証のためdomainに名前解決したい限定公開のプライベートゾーンのDNS名を指定します。
今回はそのゾーンのCNAMEに公開ゾーンのDNSを指定していますので、引くことができたら
CNAMEの先の公開ゾーンのAレコードを取得します。

main.pyrequirements.txtのソースが用意できたらzipファイルに圧縮してCloud Storageバケットへアップロードします。

実行してみる

サーバレスVPCアクセスコネクタ、Cloud Run FunctionsがデプロイできたらCloud Run Functionsを実行します。
すると以下の結果が返却されます(IPやドメイン名はマスクしています)。

{"a_records":["***.***.***.***"],"cname_target":"公開ゾーンのドメイン名.","domain":"限定公開ゾーンのドメイン名"}

限定公開のプライベートゾーンを無事名前解決できていることがわかりました。
それでは今度はCloud Run FunctionsのサーバレスVPCアクセスコネクタを設定しない状態で名前解決できるかどうかも試してみます。

サーバレスVPCアクセスコネクタを外してデプロイする

外し方は先ほどのFunctionsのデプロイ用TerraformコードからサーバレスVPCアクセスコネクタの行をコメントアウト(または削除)してterraform applyするだけです。

  service_config {
    max_instance_count  = 1
    available_memory    = "256M"
    timeout_seconds     = 60
    #vpc_connector       = "サーバレスVPCアクセスコネクタ名"
  }

サーバレスVPCアクセスコネクタを外してデプロイをすると詳細タブからは以下の通りに表示されます。
スクリーンショット 2025-01-10 0.33.01.png

さてさてデプロイもできたのでCloud Run Functionsを実行してみます。
実行すると以下が出力されました。

Domain does not exist.

これは名前解決結果がNXDOMAIN=ドメインが見つからなかったことに起因します。
検証用スクリプトの以下の行の例外処理です。

    except dns.resolver.NXDOMAIN:
        # ドメインが存在しない場合
        return 'Domain does not exist.', 404

このことから、限定公開のプライベートゾーンの名前解決をCloud Run Functionsで行うためにはサーバレスVPCアクセスコネクタ(またはDirect VPC Egress)を設定する必要がある
と体で覚えることができました。

所感

今回の検証では、以下の点が明確に確認できました。

  • サーバレスVPCアクセスコネクタを設定した場合:
    • 限定公開プライベートゾーンの名前解決が正常に機能し、CNAME を介して公開ゾーンの A レコードを取得できた。
  • サーバレスVPCアクセスコネクタを設定しなかった場合:
    • 限定公開プライベートゾーンの名前解決が失敗し、NXDOMAIN が返された。
      上記よりCloud Run Functions を使用して限定公開プライベートゾーンの名前解決を行う場合には、サーバレスVPCアクセスコネクタ(または Direct VPC Egress)が必要であることがよくわかりました。
      リファレンスにはもちろん記載されていることではありますが、実際に試してみたかったのでやってみました。

それではまた、ナマステー

参考

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/cloudfunctions2_function

https://cloud.google.com/functions/docs/networking/connecting-vpc?hl=ja

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.